package com.gitee.wsl.math.matrix



open class Matrix:Iterable<Float> {
    /** number of rows  */
    var m_nRows = 0

    /** number of columns  */
    var m_nCols = 0

    /** 2-d array of values  */
    var m_aValues: Array<FloatArray>

    /**
     * Default constructor.
     */
    constructor() {
        m_aValues = emptyArray()
    }

    /**
     * Constructor.
     *
     * @param rowCount
     * the number of rows
     * @param colCount
     * the number of columns
     */
    constructor(rowCount: Int, colCount: Int) {
        m_nRows = if (rowCount > 0) rowCount else 1
        m_nCols = if (colCount > 0) colCount else 1
        m_aValues = Array(m_nRows) { FloatArray(m_nCols) }
    }

    /**
     * Get the row count.
     *
     * @return the row count
     */
    fun rowCount(): Int {
        return m_nRows
    }

    /**
     * Get the column count.
     *
     * @return the column count
     */
    fun columnCount(): Int {
        return m_nCols
    }


    /**
     * Get the value of element [r,c] in the matrix.
     *
     * @param r
     * the row index
     * @param c
     * the column index
     * @return the value
     * @throws MatrixException
     * for an invalid index
     */
    @Throws(MatrixException::class)
    fun at(r: Int, c: Int): Float {
        if (r < 0 || r >= m_nRows || c < 0 || c >= m_nCols) {
            throw MatrixException(MatrixException.INVALID_INDEX)
        }
        return m_aValues[r][c]
    }

    operator fun get(i: Int, j: Int) = at(i,j)

    /**
     * Get a row of this matrix.
     *
     * @param r
     * the row index
     * @return the row as a row vector
     * @throws MatrixException
     * for an invalid index
     */
    @Throws(MatrixException::class)
    fun getRow(r: Int): RowVector {
        if (r < 0 || (r >= m_nRows)) {
            throw MatrixException(MatrixException.INVALID_INDEX)
        }
        val rv = RowVector(m_nCols)
        for (c in 0 until m_nCols) {
            rv.m_aValues[0][c] = m_aValues[r][c]
        }
        return rv
    }

    /**
     * Get a column of this matrix.
     *
     * @param c
     * the column index
     * @return the column as a column vector
     * @throws MatrixException
     * for an invalid index
     */
    @Throws(MatrixException::class)
    fun getColumn(c: Int): ColumnVector {
        if (c < 0 || c >= m_nCols) {
            throw MatrixException(MatrixException.INVALID_INDEX)
        }
        val cv = ColumnVector(m_nRows)
        for (r in 0 until m_nRows) {
            cv.m_aValues[r][0] = m_aValues[r][c]
        }
        return cv
    }

    /**
     * Copy the values of this matrix.
     *
     * @return the values
     */
    fun values(): Array<FloatArray> {
        return m_aValues
    }

    /**
     * Copy the values of this matrix.
     *
     * @return the copied values
     */
    fun copyValues2D(): Array<FloatArray> {
        val v = Array(m_nRows) {
            FloatArray(
                m_nCols
            )
        }
        for (r in 0 until m_nRows) {
            for (c in 0 until m_nCols) {
                v[r][c] = m_aValues[r][c]
            }
        }
        return v
    }

    override operator fun iterator(): Iterator<Float> = object : Iterator<Float> {
        private var col: Int = 0
        private var row: Int = 0

        override fun next(): Float {
            val v=this@Matrix[row,col]
            row++
            if(row>=m_nRows){
                row=0
                if(col < m_nCols)
                    col++
            }
            return v
        }

        override fun hasNext(): Boolean = row*col < m_nRows * m_nCols
    }

    fun rowIterator(): Iterator<RowVector> = object : Iterator<RowVector> {
        private var row: Int = 0
        override fun next(): RowVector {
            val v=getRow(row++)
            return v
        }
        override fun hasNext(): Boolean = row < m_nRows
    }

    fun columnIterator(): Iterator<ColumnVector> = object : Iterator<ColumnVector> {
        private var col: Int = 0
        override fun next(): ColumnVector {
            val v=getColumn(col++)
            return v
        }
        override fun hasNext(): Boolean = col < m_nCols
    }

    fun dimensions(): Pair<Int, Int> {
        return Pair(m_nRows, m_nCols)
    }

    fun isSquare(): Boolean {
        val (d1, d2) = dimensions()
        return d1 == d2
    }


    /**
     * Return the transpose of this matrix.
     *
     * @return the transposed matrix
     */
   /* fun transpose(): MutableMatrix {
        val tv = Array(m_nCols) {
            FloatArray(
                m_nRows
            )
        }

        // transposed values
        // Set the values of the transpose.
        for (r in 0 until m_nRows) {
            for (c in 0 until m_nCols) {
                tv[c][r] = m_aValues[r][c]
            }
        }
        return MutableMatrix(tv)
    }*/

    /**
     * Add another matrix to this matrix.
     *
     * @param m
     * the matrix addend
     * @return the sum matrix
     * @throws MatrixException
     * for invalid size
     */
    @Throws(MatrixException::class)
    fun add(m: MutableMatrix): MutableMatrix {
        // Validate m's size.
        if (m_nRows != m.m_nRows && m_nCols != m.m_nCols) {
            throw MatrixException(MatrixException.INVALID_DIMENSIONS)
        }
        val sv = Array(m_nRows) {
            FloatArray(m_nCols)
        } // sum values

        // Compute values of the sum.
        for (r in 0 until m_nRows) {
            for (c in 0 until m_nCols) {
                sv[r][c] = m_aValues[r][c] + m.m_aValues[r][c]
            }
        }
        return MutableMatrix(sv)
    }

    /**
     * Subtract another matrix from this matrix.
     *
     * @param m
     * the matrix subrrahend
     * @return the difference matrix
     * @throws MatrixException
     * for invalid size
     */
    @Throws(MatrixException::class)
    fun subtract(m: MutableMatrix): MutableMatrix {
        // Validate m's size.
        if (m_nRows != m.m_nRows &&  m_nCols != m.m_nCols) {
            throw MatrixException(MatrixException.INVALID_DIMENSIONS)
        }
        val dv = Array(m_nRows) {
            FloatArray(
                m_nCols
            )
        } // difference values

        // Compute values of the difference.
        for (r in 0 until m_nRows) {
            for (c in 0 until m_nCols) {
                dv[r][c] = m_aValues[r][c] - m.m_aValues[r][c]
            }
        }
        return MutableMatrix(dv)
    }

    /**
     * Multiply this matrix by a constant.
     *
     * @param k
     * the constant
     * @return the product matrix
     */
    fun multiply(k: Float): MutableMatrix {
        val pv = Array(m_nRows) {
            FloatArray(
                m_nCols
            )
        } // product values

        // Compute values of the product.
        for (r in 0 until m_nRows) {
            for (c in 0 until m_nCols) {
                pv[r][c] = k * m_aValues[r][c]
            }
        }
        return MutableMatrix(pv)
    }

    /**
     * Multiply this matrix by another matrix.
     *
     * @param m
     * the matrix multiplier
     * @return the product matrix
     * @throws MatrixException
     * for invalid size
     */
    @Throws(MatrixException::class)
    fun multiply(m: MutableMatrix): MutableMatrix {
        // Validate m's dimensions.
        if (m_nCols != m.m_nRows) {
            throw MatrixException(MatrixException.INVALID_DIMENSIONS)
        }
        val pv = Array(m_nRows) {
            FloatArray(
                m.m_nCols
            )
        } // product values

        // Compute values of the product.
        for (r in 0 until m_nRows) {
            for (c in 0 until m.m_nCols) {
                var dot = 0f
                for (k in 0 until m_nCols) {
                    dot += m_aValues[r][k] * m.m_aValues[k][c]
                }
                pv[r][c] = dot
            }
        }
        return MutableMatrix(pv)
    }

    /**
     * Multiply this matrix by a column vector: this*cv
     *
     * @param cv
     * the column vector
     * @return the product column vector
     * @throws MatrixException
     * for invalid size
     */
    @Throws(MatrixException::class)
    fun multiply(cv: ColumnVector): ColumnVector {
        // Validate cv's size.
        if (m_nRows != cv.m_nRows) {
            throw MatrixException(MatrixException.INVALID_DIMENSIONS)
        }
        val pv = FloatArray(m_nRows) // product values

        // Compute the values of the product.
        for (r in 0 until m_nRows) {
            var dot = 0f
            for (c in 0 until m_nCols) {
                dot += m_aValues[r][c] * cv.m_aValues[c][0]
            }
            pv[r] = dot
        }
        return ColumnVector(pv)
    }

    /**
     * Multiply a row vector by this matrix: rv*this
     *
     * @param rv
     * the row vector
     * @return the product row vector
     * @throws MatrixException
     * for invalid size
     */
    @Throws(MatrixException::class)
    fun multiply(rv: RowVector): RowVector {
        // Validate rv's size.
        if (m_nCols != rv.m_nCols) {
            throw MatrixException(MatrixException.INVALID_DIMENSIONS)
        }
        val pv = FloatArray(m_nRows) // product values

        // Compute the values of the product.
        for (c in 0 until m_nCols) {
            var dot = 0f
            for (r in 0 until m_nRows) {
                dot += rv.m_aValues[0][r] * m_aValues[r][c]
            }
            pv[c] = dot
        }
        return RowVector(pv)
    }

    /**
     * Print the matrix values.
     *
     * @param width
     * the column width
     * @param aPS
     * the print stream to write on. May not be `null`.
     */
    /*fun print(width: Int, ar: PrintStream) {
        for (r in 0 until m_nRows) {
            ar.print("Row  0 ")
            ar.print("${r + 1}  ")
            ar.print(":")
            for (c in 0 until m_nCols) {
                ar.print(" ${m_aValues[r][c]} ")
            }
            ar.println()
        }
    }*/
}